#ifndef CVM_NET_H
#define CVM_NET_H

#pragma unmanaged
extern "C" {
	#include "../cvm_c/types.h"
	#include "../cvm_c/constants.h"
}
#pragma managed

namespace CVM
{
	using namespace System::Collections::Generic;
	using namespace System::Diagnostics;
	using System::String;

	public enum class OpCode
	{
		#define OPCODE(OP, DESC) Op_##OP,
		#include "../cvm_c/opcodes.h"
	};

	public enum class TypeFlags
	{
		Object = ObjectType,
		Float = FloatType,
		Fun = FunType,
		Cont = ContType,
		Table = TableType,
		Store = StoreType,
		Blob = BlobType,
		Int = IntType,
	};

	public value class Op
	{
		byte* ptr;
	public:
		static System::String^ GetName(OpCode opcode);
		static System::String^ GetDesc(OpCode opcode);
		static int GetNumBytes(OpCode opcode);

		Op(byte* x) : ptr(x) { }
		property OpCode Code { OpCode get() { return (OpCode)*ptr; } }
		property System::String^ Name { System::String^ get() { return GetName(Code); } }
		property System::String^ Desc { System::String^ get() { return GetDesc(Code); } }
		property int NumBytes { int get() { return GetNumBytes(Code); } }
		property byte Byte[int] { byte get(int n) { return ptr[n]; } }
		System::String^ ToString() new;
	};

	value class Val;

	public value class Object
	{
		::Object* obj;
	public:
		Object(::Object* x) : obj(x) { }		
		Val operator[](int n);
		void Push(Val v);
		void Pop();
		property bool Null { bool get() { return obj == NULL; } }
		property int Count { int get(); }
		property uint Id { uint get() { return (uint)obj; } }
		property byte AllFlags { byte get() { return obj->flags; } }
		property byte Flags { byte get() { return obj->flags & 0xF; } }
		property byte Type { byte get() { return obj->flags >> 4; } }
		property bool Deleted { bool get() { return Flags & ::Deleted; } }
		property byte Refs { byte get() { return obj->refs; } }
		property System::String^ TypeString { System::String^ get(); }
		property array<byte>^ Object::ArrayBytes { array<byte>^ get(); }
	};

	public value class KeyValue
	{
		uint key;
		::Val* val;
	public:
		KeyValue(::TableIter* i) {
			key = i->cur->key;
			val = i->cur->value;
		}
		property uint Key { uint get(); }
		property Val Value { Val get(); }
	};

	public value class Table 
	{
		::Object* obj;
	public:
		Table(::Object* x) : obj(x) { }		
		void Add(uint key, Val val);
		bool Contains(uint key);
		Val operator[](uint Key);
		property List<KeyValue>^ KeyValues { List<KeyValue>^ get(); } 
		property uint Id { uint get() { return (uint)obj; } }
	};


	public value class Val
	{
	internal:
		::Val* val;
	public:
		Val(::Val* x) : val(x) { }		
		int ToInt();
		float ToFloat();
		CVM::Object ToObject();
		Table ToTable();
		property bool IsInt { bool get(); } 
		property uint Id { uint get() { return (uint)val; } }
		property TypeFlags Type { TypeFlags get(); } 
	};

	public ref class VM
	{
		List<Op>^ ops;
		List<CVM::Object>^ ks;
	public:
		::VM* vm;
		VM();
		~VM();
		bool Next();
		void Run();
		void Dispatch(byte code);
		void Load(array<byte>^ data);		
		Val At(int n) { return Val(vm->sp[-n]); }
		Val AuxAt(int n) { return Val(vm->ap[n]); }
		property int Count { int get(); }
		property int AuxCount { int get(); }
		property int IP { int get() { return vm->ip - vm->code; } }
		property List<Op>^ Ops { List<Op>^ get() { return ops; } }
		property List<CVM::Object>^ Constants { List<CVM::Object>^ get() { return ks; } }
		property Op CurOp { Op get(); }
		List<CVM::Object>^ GetHeapMemory();
		property String^ CodeString { String^ get(); }
		property List<Val>^ Stack { List<Val>^ get(); } 
		property List<Val>^ AuxStack { List<Val>^ get(); } 
	};
}

#endif